/* Implements a String inline cellar stack machine */
/* Created 16.10.2002 T. Milius
   Changed 06.10.2004 T. Milius */
/* (c) Copyright 2002-2004 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* ANSI-C */

#ifndef cellar_stack_h
#define cellar_stack_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- own ---------- */
#include "settings.h"

/* !!!!!!!!!! definitions !!!!!!!!!! */
/* to clarify reading */
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

/* !!!!!!!!!! data structures !!!!!!!!!! */
/* Addresses of Variables

   From 0x00000000 to 0x0000000F there are variables which are manipulated during
   runtime of the job by the dumper (read only)

   0x00000000 - Number of remaining copies
   0x00000001 - Number of actual stage (updated before each data output)
   0x00000002 - Number of actual vertical interlace (updated before each data output)
   0x00000003 - Number of actual horizontal interlace (updated before each data output)
   0x00000004 - bytes of all data sent in the next data block (updated before each data output).
   0x00000005 - bytes per line filled (updated before each data output).
                Excluding blank parts on the right side. Attention: This are bytes not pixel.
   0x00000006 - Number actual page of job
   0x00000007 - Number of the job since start of the dumper for unique identification
                purposes.
   0x00000008 - 0x0000000F reserved

   From 0x00000010 to 0x0000004F there are variables which are determined by the printing
   system (read only)

   0x00000010 - Total number of vertical interlaces
   0x00000011 - Total number of horizontal interlaces
   0x00000012 - Total number of colours (not implemented yet)
   0x00000013 - Total number of dots per print head
   0x00000014 - vertical dots per inch
   0x00000015 - horizontal dots per inch
   0x00000016 - vertical offset in pixel at beginning of the page
   0x00000017 - horizontal left side offset in pixel at beginning of the line
   0x00000018 - page width in inch/10000
   0x00000019 - page height in inch/10000
   0x0000001A - page range top left vertical position in inch/10000
   0x0000001B - page range top left horizontal position in inch/10000
   0x0000001C - page range top left vertical position in inch/10000
   0x0000001D - page range top left horizontal position in inch/10000
   0x0000001E - 0x0000004F reserved

   From 0x00000050 to 0x0000007F there are variables which are determining the dumper behaviour
   (read/write depending on variable):

   0x00000050 - Total number of stages (read only)
   0x00000051 - data compression format (read/write)
   0x00000052 - vertical offset in pixel at beginning of the page not handled by control sequences (read/write).
                Automatically updated at start of every page. It makes only sense to change it at the
                page start sequence
   0x00000053 - Remaining copies of page (read/write)
                Automatically updated at start of every page.
   0x00000054 - number of printer catridges (read only)
   0x00000055 - Bits per printer pixel (read only)
   0x00000056 - Printer Mode (read only)
   0x00000057 - Calibration (read/write)
   0x00000058 - 0x0000007F reserved

   From 0x00000080 to 0x000000FF there are variables free for program usage. They are
   uninitialized at beginning of print job but are kept until the end of the print
   job (read/write):

   0x00000080 - 0x0000008F free useable variables
   0x00000090 - 0x000000FF reserved

  */

struct cellar_stack_variable_struct {
struct {
  long remaining_copies;
  long act_stage;
  long act_interlace_vertical;
  long act_interlace_horizontal;
  long byte_data_amount;
  long bytes_per_line_filled;
  long act_page;
  long job_number;
  } job_state;
struct {
  long no_of_interlaces_vertical;
  long no_of_interlaces_horizontal;
  long no_of_colours; /* ??? */
  long dots_of_print_head;
  /* dpi */
  long resolution_vertical;
  long resolution_horizontal;
  /* offsets to first print position in pixel */
  long page_offset_vertical;
  long line_offset_horizontal;
  /* Page info in inch/10000 */
  long page_height;
  long page_width;
  long page_range_top_left_vertical;
  long page_range_top_left_horizontal;
  long page_range_bottom_right_vertical;
  long page_range_bottom_right_horizontal;
  } printing_system;
struct {
  long no_of_stages;
  /* 0 - uncompressed little endian (default)
     1 - uncompressed big endian (not implemented already)
     2 - Run length encoded (not implemented already)
     3 - Tiff encoded (not implemented already) */
  long compression_format;
  /* in pixel.
     Set automatically at every page start to same value as page_offset_vertical
     if skipping is done by control sequence this must be reduced inside
     page start sequence every time */
  long remaining_page_offset_vertical;
  long remaining_copies;
  long no_of_printer_catridges;
  long bits_per_printer_pixel;
  /* 0 - generation of printer data (default)
     1 - output of control sequences only
     2 - Generation of a 24 bit Sprite. Input_Colour=Output_Colour
     3 - Generation of a 24 bit Sprite. Unsing printer colours dithered
     4 - Generation of a palette Sprite. Unsing printer colours dithered
         but mapped by a palette. If number of printer colours is
         bigger than 256 program will switch automatically to
         mode 3. (not implemented yet) */
  long printer_mode;
  long calibration;
  } dumper;
struct {
  long variable[16];
  } locale;
/* needeed for some commands */
/* Printer specific settings */
char settings_file_name[1024];
char output_file_name[1024];
/* Debugging purposes */
int stack_trace_flag;
int output_file;
};

/* !!!!!!!!!! support functions !!!!!!!!!! */
int stack_trace(struct cellar_stack_variable_struct *environment,
                int command,
                char *command_name,
                int stack_condition,
                long *stack)
{
char trace_string[500];
_kernel_swi_regs regs;


sprintf(trace_string,
        "Stack operation: %d %s Condition: %d Registers: %ld %ld %ld %ld\n",
        command,
        command_name,
        stack_condition,
        stack[0],
        stack[1],
        stack[2],
        stack[3]);
regs.r[0]=2;
regs.r[1]=environment->output_file;
regs.r[2]=(int) trace_string;
regs.r[3]=strlen(trace_string);
_kernel_swi(OS_GBPB, &regs, &regs);
return TRUE;
}

long *get_stack_variable_address(struct cellar_stack_variable_struct *environment,
                                 int no_of_variable,
                                 int *writeable_flag)
{

if (writeable_flag) {
  *writeable_flag=FALSE;
  }
switch(no_of_variable) {
  case 0: {
    return &environment->job_state.remaining_copies;
    }
  break;
  case 1: {
    return &environment->job_state.act_stage;
    }
  break;
  case 2: {
    return &environment->job_state.act_interlace_vertical;
    }
  break;
  case 3: {
    return &environment->job_state.act_interlace_horizontal;
    }
  break;
  case 4: {
    return &environment->job_state.byte_data_amount;
    }
  break;
  case 5: {
    return &environment->job_state.bytes_per_line_filled;
    }
  break;
  case 6: {
    return &environment->job_state.act_page;
    }
  break;
  case 7: {
    return &environment->job_state.job_number;
    }
  break;
  case 16: {
    return &environment->printing_system.no_of_interlaces_vertical;
    }
  break;
  case 17: {
    return &environment->printing_system.no_of_interlaces_horizontal;
    }
  break;
  case 18: {
    return &environment->printing_system.no_of_colours;
    }
  break;
  case 19: {
    return &environment->printing_system.dots_of_print_head;
    }
  break;
  case 20: {
    return &environment->printing_system.resolution_vertical;
    }
  break;
  case 21: {
    return &environment->printing_system.resolution_horizontal;
    }
  break;
  case 22: {
    return &environment->printing_system.page_offset_vertical;
    }
  break;
  case 23: {
    return &environment->printing_system.line_offset_horizontal;
    }
  break;
  case 24: {
    return &environment->printing_system.page_width;
    }
  break;
  case 25: {
    return &environment->printing_system.page_height;
    }
  break;
  case 26: {
    return &environment->printing_system.page_range_top_left_vertical;
    }
  break;
  case 27: {
    return &environment->printing_system.page_range_top_left_horizontal;
    }
  break;
  case 28: {
    return &environment->printing_system.page_range_bottom_right_vertical;
    }
  break;
  case 29: {
    return &environment->printing_system.page_range_bottom_right_horizontal;
    }
  break;
  case 80: {
    return &environment->dumper.no_of_stages;
    }
  break;
  case 81: {
    if (writeable_flag) {
      *writeable_flag=TRUE;
      }
    return &environment->dumper.compression_format;
    }
  break;
  case 82: {
    if (writeable_flag) {
      *writeable_flag=TRUE;
      }
    return &environment->dumper.remaining_page_offset_vertical;
    }
  break;
  case 83: {
    if (writeable_flag) {
      *writeable_flag=TRUE;
      }
    return &environment->dumper.remaining_copies;
    }
  break;
  case 84: {
    return &environment->dumper.no_of_printer_catridges;
    }
  break;
  case 85: {
    return &environment->dumper.bits_per_printer_pixel;
    }
  break;
  case 86: {
    return &environment->dumper.printer_mode;
    }
  break;
  case 87: {
    if (writeable_flag) {
      *writeable_flag=TRUE;
      }
    return &environment->dumper.calibration;
    }
  break;
  default: {
    if ((no_of_variable >= 128) &&
        (no_of_variable <= 143)) {
      if (writeable_flag) {
        *writeable_flag=TRUE;
        }
      return &environment->locale.variable[no_of_variable - 128];
      }
    }
  }
return NULL;
}

/* ---------- stack handling ---------- */
int initalize_stack(struct cellar_stack_variable_struct *environment,
                    int output_file,
                    int stack_trace_flag)
{
_kernel_swi_regs regs;

environment->stack_trace_flag=stack_trace_flag;
environment->output_file=output_file;
strcpy(environment->output_file_name, "");
/* Get name of output file */
regs.r[0]=7;
regs.r[1]=output_file;
regs.r[2]=NULL;
regs.r[5]=0;
_kernel_swi(OS_Args, &regs, &regs);
if ((1 - regs.r[5]) < 1024) {
   regs.r[0]=7;
   regs.r[1]=output_file;
   regs.r[2]=(int) environment->output_file_name;
   regs.r[5]=1 - regs.r[5];
   _kernel_swi(OS_Args, &regs, &regs);
   }
return TRUE;
}

int drop_stack(struct cellar_stack_variable_struct *environment)
{

return TRUE;
}

void push_stack(long *stack)
{

stack[3]=stack[2];
stack[2]=stack[1];
stack[1]=stack[0];
}

void pop_stack(long *stack)
{

stack[0]=stack[1];
stack[1]=stack[2];
stack[2]=stack[3];
}

/* !!!!!!!!!! functions !!!!!!!!!! */
/* The stack has 4 stack registers (A, B, C, D) and one condition state
   which is FALSE at beginning of a cellar stack sequence.

   Each command is 1 Byte long. Bits are numbered from 0 to 7.
   Bit 7 shows whether a command must be always executed (=1)
   or only if the condition state is TRUE (=0).

   Commands:

   C000XXXX - Load nibble.
              XXXX gives nibble value which is loaded onto the stack (register A).
              Stack is pushed before.

   C001XXXX - Set Condition.
              XXXX gives the check which is performed:
              0000 - A = 0
              0001 - A != 0
              0010 - A < 0
              0011 - A > 0
              0100 - A <= 0
              0101 - A >= 0
              0110 - Condition set to FALSE
              0111 - Condition set to TRUE
              1000 - A = B pop registers
              1001 - A != B pop registers
              1010 - A < B pop registers
              1011 - A > B pop registers
              1100 - A <= B pop registers
              1101 - A >= B pop registers
              1110 - Invert Condition
              1111 - reserved

   C010XXXX - Perform stack operation.
              XXXX gives the operation which is performed:
              0000 - B + A
              0001 - B - A
              0010 - B * A
              0011 - B / A
              0100 - B MOD A
              0101 - B POWER A
              0110 - B shifted right by A
              0111 - B shifted left by A
              1000 - INVERT A
              1001 - B OR A
              1010 - B AND A
              1011 - B EXOR A
              1100 - POP Stack (A will be dropped)
              1101 - PUSH Stack (A will be doubled)
              1110 - SWAP A B
              1111 - (B << 4) OR (A AND 0x0000000F). Useful for loading arbitrary values by Nibbles

              The operands are usually dropped from stack that means the stack will be popped acordingly
              and the result will pushed on the stack.

   C0110XXX - Write stack content register A into control sequence.
              Stack is popped afterwards.
              XXX gives the format of the output:
              000 - As humanreadable decimal number digits (0-9)
              001 - 1 Byte binary number
              100 - As humanreadable hexadecimal number lower case letters (0-9, a-f)
              101 - As humanreadable hexadecimal number upper case letters (0-9, A-F)

              and

               10 - 2 Bytes binary number
               11 - 4 Bytes binary number

              with

              0   - little endian
              1   - big endian

   C01110XX - Compare page size (high/width according variables
              0x00000018 and 0x00000019) with page size sequences
              inside the page sequence configuration table. If
              a matching entry is found then the condition is set to TRUE
              and the sequence determined by XX (0-3) is depending
              on its type (see below) either put it into the
              output sequence or its value(s) is/are pushed on the stack.
              If no matching is found then set the condition
              to FALSE.

              Inside the configuration file you can include
              page sequences in the following way:
              "page_sequence_start" shows the beginning of
              the sequence list. "page_sequence_end" terminates
              the sequence list. Between them there can be an
              arbitrary number of page sequences of the following
              format:

              decimal_page_width decimal_page_high [sequence_0 [sequence_1 [sequence_2 [sequence_3]]]]

              A sequence consists of an identifier followed by a colon:

              - V: shows a value which is pushed onto the stack followed by up to 4
                   comma separated decimal integer values (maximal signed 32 Bit)

              - S: shows a control sequence which is inserted into the printer control
                sequence. The sequence has the usual form of control strings used inside print
                edit (mixture of quoted letters and decimal numbers separated by commas, no
                cellar stack sequences are allowed in here, so 255 means really the letter 255).
                Important: No space is allowed inside the sequence as it would show the
                beginning of the next sequence.

              Note that sequence may be optionally given and S: and V: sequences can be mixed
              arbitrarily. Example:

              12345 456789 S:27,"(","C",4,0,"q",16,0,0 V:123,456

              sequence 0 for page size 12345/456789 is 27,"(","C",4,0,"q",16,0,0 and
              will be inserted into the print control sequence.

              sequence 1 gives values of 123 and 456 which will be pushed on the stack.
              123 will be pushed first onto the stack and 456 will be pushed afterwards
              onto the stack. So register B will contain 123 and register A will contain 456.

              sequence 2 and sequence 3 are not defined.

              What can you do with such a sequence:

              a. you can tell the printer the paper size (A4, letter)
              b. you can tell the printer the paper source
              c. you can tell the printer the paper type (photo/normal)

              Note: Use a trick to handle special paper types. Due to the
              absence of another possibility inside RISCOS to tell
              the paper type simply use a slightly smaller width or high
              then of normal paper. This won't influence the printing
              process.

   C0111110 - Save stack content B into variable which is determined by A.
              Stack is popped afterwards (A is dropped).

   C0111111 - Load stack with content of a variable which is determined by A.
              A is replaced by the content of te variable.

   C100XXXX - Load stack with content of a job state variable.
              Stack is pushed before.
              XXXX gives the address of the job state variable.

   C10100XX - Write name of target file into control sequence.
              No Stack operation is performed.

              XX gives the format of the output:
              00 - reserved
              01 - file name
              10 - path
              11 - path and file name

   01010100 - Write a sequence of bytes following this command into
              control sequence conditionally. The sequence is terminated
              by 11010100 which doesn't make other sense for it would
              have the same meaning as locating the same data outside a
              cellar stack sequence. A byte 11010100 inside the sequence
              can be represented by two 11010100 following after eachother.
              According to the condition the sequence is written or ignored.
              In both cases the cellar stack calculator continues with the
              command after the terminating 11010100. You can embed 11111111
              inside the sequence. It is not treatened as a cellar stack
              terminator.

   11010100 - Terminates a sequence of bytes introduced by 01010100. A
              11010100 without a 01010100 has no effect.

   11111111 - End of cellar stack sequence. The command pointer will point beyond it at end of
              cellar stack execution. This sequence is mandatory. If it is not existing
              the whole stack evaluation except changing of the variables will be ignored.

   01111111 - Conditional end of cellar stack sequence. Ecxecution stops here but sequence is skipped
              until 0xFF is found.

   All other sequences are reserved. */

unsigned char *evaluate_stack(unsigned char *stack_command,
                              unsigned char **stack_command_end,
                              unsigned char *stack_line,
                              struct cellar_stack_variable_struct *environment)
{
char number[12];
char *c;
int i;
long h;
int stack_condition;
int writeable_flag;
long stack_element[4];
long *address_of_variable;
char command_name[100];

stack_condition=FALSE;
while ((stack_command < (*stack_command_end)) &&
       (*stack_command != 0xFF) &&
       (!((*stack_command == 0x7F) &&
          (stack_condition == TRUE)))) {
  if ((((*stack_command) & 0x80) != 0) ||
      ((((*stack_command) & 0x80) == 0) &&
       (stack_condition == TRUE)) ||
       ((*stack_command) == 0x54)) {
    /* Command execution */
    if ((*stack_command & 0x70) == 0x00) {
      /* Load Nibble */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Load nibble");
        }
      push_stack(stack_element);
      stack_element[0]=(*stack_command) & 0x0F;
      }
    else if ((*stack_command & 0x70) == 0x10) {
      /* Set condition */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Set condition");
        }
      switch(*stack_command & 0x0F) {
        case 0: {
          if (stack_element[0] == 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 1: {
          if (stack_element[0] != 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 2: {
          if (stack_element[0] < 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 3: {
          if (stack_element[0] > 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 4: {
          if (stack_element[0] <= 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 5: {
          if (stack_element[0] >= 0) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        case 6: {
          stack_condition=FALSE;
          }
        break;
        case 7: {
          stack_condition=TRUE;
          }
        break;
        case 8: {
          if (stack_element[0] == stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 9: {
          if (stack_element[0] != stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 10: {
          if (stack_element[0] < stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 11: {
          if (stack_element[0] > stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 12: {
          if (stack_element[0] <= stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 13: {
          if (stack_element[0] >= stack_element[1]) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          pop_stack(stack_element);
          }
        break;
        case 14: {
          if (stack_condition == FALSE) {
            stack_condition=TRUE;
            }
          else {
            stack_condition=FALSE;
            }
          }
        break;
        }
      }
    else if ((*stack_command & 0x70) == 0x20) {
      /* Perform stack operation */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Perform stack operation");
        }
      switch(*stack_command & 0x0F) {
        case 0: {
          stack_element[1]=stack_element[1] + stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 1: {
          stack_element[1]=stack_element[1] - stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 2: {
          stack_element[1]=stack_element[1]*stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 3: {
          if (stack_element[0] != 0) {
            stack_element[1]=stack_element[1]/stack_element[0];
            pop_stack(stack_element);
            }
          }
        break;
        case 4: {
          if (stack_element[0] != 0) {
            stack_element[1]=stack_element[1]%stack_element[0];
            pop_stack(stack_element);
            }
          }
        break;
        case 5: {
          h=1;
          for (i=0; i < stack_element[1]; i++) {
            h*=stack_element[0];
            }
          stack_element[1]=h;
          pop_stack(stack_element);
          }
        break;
        case 6: {
          stack_element[1]=stack_element[1]>>stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 7: {
          stack_element[1]=stack_element[1]<<stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 8: {
          stack_element[0]=~stack_element[0];
          }
        break;
        case 9: {
          stack_element[1]=stack_element[1] | stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 10: {
          stack_element[1]=stack_element[1] & stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 11: {
          stack_element[1]=stack_element[1] ^ stack_element[0];
          pop_stack(stack_element);
          }
        break;
        case 12: {
          pop_stack(stack_element);
          }
        break;
        case 13: {
          push_stack(stack_element);
          }
        break;
        case 14: {
          h=stack_element[0];
          stack_element[0]=stack_element[1];
          stack_element[1]=h;
          }
        break;
        case 15: {
          stack_element[1]=(stack_element[1] << 4) | (stack_element[0] & 0x0000000F);
          pop_stack(stack_element);
          }
        break;
        }
      }
    else if ((*stack_command & 0x78) == 0x30) {
      /* Write stack content register into control sequence */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Write stack to control sequence");
        }
      switch(*stack_command & 0x07) {
        case 0: {
          sprintf(number, "%ld", stack_element[0]);
          c=number;
          while (*c != '\0') {
            *stack_line=*c;
            stack_line++;
            c++;
            }
          }
        break;
        case 1: {
          *stack_line=(unsigned char) (stack_element[0] & 0xFF);
          stack_line++;
          }
        break;
        case 2: {
          *stack_line=(unsigned char) ((stack_element[0]>>8) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>0) & 0xFF);
          stack_line++;
          }
        break;
        case 3: {
          *stack_line=(unsigned char) ((stack_element[0]>>24) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>16) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>8) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>0) & 0xFF);
          stack_line++;
          }
        break;
        case 4: {
          sprintf(number, "%lx", stack_element[0]);
          c=number;
          while (*c != '\0') {
            *stack_line=*c;
            stack_line++;
            c++;
            }
          }
        break;
        case 5: {
          sprintf(number, "%lX", stack_element[0]);
          c=number;
          while (*c != '\0') {
            *stack_line=*c;
            stack_line++;
            c++;
            }
          }
        break;
        case 6: {
          *stack_line=(unsigned char) ((stack_element[0]>>0) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>8) & 0xFF);
          stack_line++;
          }
        break;
        case 7: {
          *stack_line=(unsigned char) ((stack_element[0]>>0) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>8) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>16) & 0xFF);
          stack_line++;
          *stack_line=(unsigned char) ((stack_element[0]>>24) & 0xFF);
          stack_line++;
          }
        break;
        }
      pop_stack(stack_element);
      }
    else if ((*stack_command & 0x7C) == 0x38) {
      int no_of_parameters;
      long page_height;
      long page_width;
      int file_section;
      char actual_data_line[512];
      char sequence[4][512];
      char *c, *value_start;
      FILE *settings_file;

      /* Compare page size */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Compare page size");
        }
      h=(*stack_command & 0x03);
      stack_condition=FALSE;
      if ((settings_file=fopen(environment->settings_file_name, "r")) != NULL) {
        file_section=FILE_SECTION_INDETERMINED;
        actual_data_line[511]='\0';
        while((!feof(settings_file)) &&
              (!stack_condition)) {
          if (fgets(actual_data_line,
                    511,
                    settings_file)) {
            /* Ignore comments */
            if (strncmp(actual_data_line,
                        FILE_COMMENT,
                        strlen(FILE_COMMENT)) != 0) {
              if (strncmp(actual_data_line,
                          FILE_PAGE_SEQUENCE_START,
                          strlen(FILE_PAGE_SEQUENCE_START)) == 0) {
                file_section=FILE_SECTION_PAGE_SEQUENCE;
                }
              else if (strncmp(actual_data_line,
                               FILE_PAGE_SEQUENCE_END,
                               strlen(FILE_PAGE_SEQUENCE_END)) == 0) {
                file_section=FILE_SECTION_INDETERMINED;
                }
              else {
                switch(file_section) {
                  case FILE_SECTION_PAGE_SEQUENCE: {
                    if ((no_of_parameters=sscanf(actual_data_line,
                                                 "%ld %ld %s %s %s %s",
                                                 &page_width,
                                                 &page_height,
                                                 sequence[0],
                                                 sequence[1],
                                                 sequence[2],
                                                 sequence[3])) >= 2) {
                      if ((page_width == environment->printing_system.page_width) &&
                          (page_height == environment->printing_system.page_height)) {
                        stack_condition=TRUE;
                        /* Evaluate only if required sequence exists */
                        if (no_of_parameters >= (h+3)) {
                          c=sequence[h] + 2;
                          value_start=c;
                          /* Look whether it is a value or a sequence */
                          if (strncmp(sequence[h],
                                      FILE_PAGE_SEQUENCE_VALUE,
                                      strlen(FILE_PAGE_SEQUENCE_VALUE)) == 0) {
                            /* Scan all values separated by comma and put them
                               onto the stack */
                            while (*c != '\0') {
                              if (*c == ',') {
                                *c='\0';
                                push_stack(stack_element);
                                stack_element[0]=atol(value_start);
                                value_start=c+1;
                                }
                              c++;
                              }
                            push_stack(stack_element);
                            stack_element[0]=atol(value_start);
                            }
                          else if (strncmp(sequence[h],
                                           FILE_PAGE_SEQUENCE_SEQUENCE,
                                           strlen(FILE_PAGE_SEQUENCE_SEQUENCE)) == 0) {
                            /* Scan character by character */
                            while (*c != '\0') {
                              if (*c == ',') {
                                *c='\0';
                                if (*value_start == '\"') {
                                  *stack_line=*(value_start+1);
                                  stack_line++;
                                  }
                                else {
                                  *stack_line=atoi(value_start);
                                  stack_line++;
                                  }
                                value_start=c+1;
                                }
                              c++;
                              }
                            if (*value_start == '\"') {
                              *stack_line=*(value_start+1);
                              stack_line++;
                              }
                            else {
                              *stack_line=atoi(value_start);
                              stack_line++;
                              }
                            }
                          }
                        }
                      }
                    }
                  break;
                  }
                }
              }
            }
          }
        fclose(settings_file);
        }
      }
    else if ((*stack_command & 0x7F) == 0x3E) {
      /* Save stack content B into variable which is determined by A. */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Save stack to variable");
        }
      if ((address_of_variable=get_stack_variable_address(environment,
                                                          stack_element[0],
                                                          &writeable_flag)) != NULL) {
        if (writeable_flag) {
          *address_of_variable=stack_element[1];
          }
        pop_stack(stack_element);
        }
      }
    else if ((*stack_command & 0x7F) == 0x3F) {
      /* Load stack with content of a variable which is determined by A. */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Load stack with content of a variable");
        }
      if ((address_of_variable=get_stack_variable_address(environment,
                                                          stack_element[0],
                                                          NULL)) != NULL) {
        stack_element[0]=*address_of_variable;
        }
      }
    else if ((*stack_command & 0x70) == 0x40) {
      /* Load stack with content of a job state variable. */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Load stack with content of a job state variable");
        }
      if ((address_of_variable=get_stack_variable_address(environment,
                                                          *stack_command & 0x0F,
                                                          NULL)) != NULL) {
        push_stack(stack_element);
        stack_element[0]=*address_of_variable;
        }
      }
    else if ((*stack_command & 0x7C) == 0x50) {
      /* Write file name to control sequence */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "Write file name to control sequence");
        }
      if (strlen(environment->output_file_name) > 0) {
        switch(*stack_command & 0x03) {
          case 1: {
            c=&environment->output_file_name[strlen(environment->output_file_name)]-1;
            while (*c != '.') {
              c--;
              }
            c++;
            while (*c != '\0') {
              *stack_line=*c;
              stack_line++;
              c++;
              }
            }
          break;
          case 2: {
            char *ep;

            ep=&environment->output_file_name[strlen(environment->output_file_name)]-1;
            while (*ep != '.') {
              ep--;
              }
            c=environment->output_file_name;
            while (c < ep) {
              *stack_line=*c;
              stack_line++;
              c++;
              }
            }
          break;
          case 3: {
            c=environment->output_file_name;
            while (*c != '\0') {
              *stack_line=*c;
              stack_line++;
              c++;
              }
            }
          break;
          }
        }
      }
    else if (*stack_command == 0x54) {
      /* Embed sequence */
      /* evaluated everytime because of forwarding of the
         command pointer. */
      if (environment->stack_trace_flag) {
        if (stack_condition == TRUE) {
          strcpy(command_name, "Embed sequence");
          }
         else {
          strcpy(command_name, "Ignored");
          }
        }
      stack_command++;
      while (stack_command < (*stack_command_end)) {
        if ((*stack_command == 0xD4) &&
            (((stack_command + 1) >= (*stack_command_end)) ||
             (*(stack_command + 1) != 0xD4))) {
          break;
          }
        if (*stack_command == 0xD4) {
          /* Skip one of the both 0xD4 */
          stack_command++;
          }
        if (stack_condition == TRUE) {
          /* copy value into sequence */
          *stack_line=*stack_command;
          stack_line++;
          }
        stack_command++;
        }
      }
    else if (*stack_command == 0xD4) {
      /* End of sequence */
      if (environment->stack_trace_flag) {
        strcpy(command_name, "End of sequence. Ignored");
        }
      }
    if (environment->stack_trace_flag) {
      stack_trace(environment,
                  *stack_command,
                  command_name,
                  stack_condition,
                  stack_element);
      }
    }
  else {
    if (environment->stack_trace_flag) {
      stack_trace(environment,
                  *stack_command,
                  "ignored",
                  stack_condition,
                  stack_element);
      }
    }
  /* Evaluate next command */
  stack_command++;
  }
if (stack_command >= (*stack_command_end)) {
  /* Incorrectly terminated */
  *stack_command_end=stack_command;
  return NULL;
  }
/* Scan to end of stack command sequence */
while ((stack_command < (*stack_command_end)) &&
       (*stack_command != 0xFF)) {
  stack_command++;
  }
if (stack_command >= (*stack_command_end)) {
  /* Incorrectly terminated */
  *stack_command_end=stack_command;
  return NULL;
  }
/* Skip 0xFF at end */
stack_command++;
*stack_command_end=stack_command;
return stack_line;
}

#endif
